<?php
class SD_DownLoad {
	private $mDownStart;
	private $mFileSize;
	private $mFileHandle;
	private $mFilePath;
	private $mFileName;
	public function __construct() { }
	public function Down($pFilePath, $pFileName = '') {
		$this->mFilePath = $pFilePath;
		if(!$this->IniFile()) $this->SendError();
		$this->mFileName = empty($pFileName) ? $this->GetFileName() : $pFileName;

		$this->IniFile();
		$this->SetStart();
		$this->SetHeader();

		$this->Send();
	}
	private function IniFile() {
		if(!is_file($this->mFilePath)) return false;
		$this->mFileHandle = fopen($this->mFilePath, 'rb');
		$this->mFileSize = filesize($this->mFilePath);
		return true;
	}
	private function SetStart() {
		if (!empty($_SERVER['HTTP_RANGE']) && preg_match("/^bytes=([\d]?)-([\d]?)$/i", $_SERVER['HTTP_RANGE'], $match)) {
			if(empty($match[1])) $this->mDownStart = $match[1];
			fseek($this->mFileHandle, $this->mDownStart);
		} else {
			$this->mDownStart = 0;
		}
	}
	private function SetHeader() {
		@header("Cache-control: public");
		@header("Pragma: public");
		Header("Content-Length: " . ($this->mFileSize - $this->mDownStart));
		if ($this->mDownStart > 0) {
			@Header("HTTP/1.1 206 Partial Content");
			Header("Content-Ranges: bytes" . $this->mDownStart . "-" . ($this->mFileSize - 1) . "/" . $this->mFileSize);
		}
		else {
			Header("Accept-Ranges: bytes");
		}
		@header("Content-Type: application/octet-stream");
		@header("Content-Disposition: attachment;filename=" . $this->mFileName);
	}
	private function GetFileName() {
		return basename ($this->mFilePath);
	}

	private function Send() {
		fpassthru($this->mFileHandle);
	}
	public function SendError() {
		@header("HTTP/1.0 404 Not Found");
		@header("Status: 404 Not Found");
		exit();
	}
}
$down = new SD_DownLoad();
$down->Down('a.php','test.php');
